// 手写Promise
const PENDING = "pendig";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
/**
 * 将一个任务添加到微队列
 * @param {*} callback
 */
function runMicorTask(callback) {
  // Node环境
  if (globalThis.process && globalThis.process.nextTick) {
    globalThis.process.nextTick(callback);
  } else if (globalThis.window && globalThis.window.MutationObserver) {
    const obs = new MutationObserver(callback);
    const p = document.createElement("p");
    obs.observe(p, { childList: true });
    p.innerHTML = "1";
  } else {
    setTimeout(callback, 0);
  }
}

/**
 * 判断一个值是否是Promise
 * @param {*} obj
 */
function isPromise(obj) {
  return !!(obj && typeof obj === "object" && typeof obj.then === "function");
}

class MyPromise {
  constructor(executor) {
    this._status = PENDING;
    this._handlers = [];
    try {
      executor(this._resolve.bind(this), this._reject.bind(this));
    } catch (error) {
      this._reject(error);
    }
  }
  /**
   * 更新Promise状态
   * @param {*} status
   * @param {*} value
   */
  _chaneStatusValue(status, value) {
    if (this._status != PENDING) return;
    this._status = status;
    this._value = value;
    this._runHandlers();
  }
  /**
   * 更新状态为fulfilled
   * @param {*} data
   */
  _resolve(data) {
    this._chaneStatusValue(FULFILLED, data);
  }
  /**
   * 更新状态为reject
   * @param {*} reason
   */
  _reject(reason) {
    this._chaneStatusValue(REJECTED, reason);
  }
  /**
   * 将执行函数放入队列中
   * @param {*} param0
   */
  _pushHandler(onFulfilled, onRejected, resolve, reject) {
    this._handlers.push({
      callback: onFulfilled,
      status: FULFILLED,
      resolve,
      reject,
    });
    this._handlers.push({
      callback: onRejected,
      status: REJECTED,
      resolve,
      reject,
    });

    this._runHandlers();
  }

  /**
   * 实现Promise A+ 规范
   */
  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      this._pushHandler(onFulfilled, onRejected, resolve, reject);
    });
  }
  /**
   * 执行队列中的回调
   * @returns
   */
  _runHandlers() {
    if (this._status === PENDING) return;

    if (this._status === REJECTED) {
      const rejectHandler = this._handlers.find(
        (handler) =>
          handler.status === REJECTED && typeof handler.callback === "function"
      );
      if (!rejectHandler) {
        throw new Error("没有对错误进行处理");
        return;
      }
    }
    while (this._handlers[0]) {
      const handler = this._handlers.shift();
      this._runOneHandler(handler);
    }
  }
  /**
   * 运行一个队列中的回调
   */
  _runOneHandler({ status, callback, resolve, reject }) {
    runMicorTask(() => {
      if (this._status === status) {
        if (typeof callback !== "function") {
          this._status === FULFILLED
            ? resolve(this._value)
            : reject(this._value);
          return;
        }
        try {
          const result = callback(this._value);
          if (isPromise(result)) {
            result.then(resolve, reject);
          } else {
            resolve(result);
          }
        } catch (error) {
          reject(error);
        }
      }
    });
  }

  static resolve(data) {
    if (data instanceof MyPromise) {
      return data;
    }
    return new MyPromise((resolve, reject) => {
      if (isPromise(data)) {
        return data.then(resolve, reject);
      } else {
        resolve(data);
      }
    });
  }

  static reject(reason) {
    return new MyPromise((_, reject) => {
      reject(reason);
    });
  }
  catch(onRejected) {
    return this.then(null, onRejected);
  }

  finally(onSettled) {
    return this.then(
      (data) => {
        onSettled();
        return data;
      },
      (reason) => {
        onSettled();
        return reason;
      }
    );
  }

  static all(pros) {
    return new MyPromise((resolve, reject) => {
      let result = [];
      let count = 0;
      let fulfilledCount = 0;
      for (let pro of pros) {
        let index = count;
        count++;
        MyPromise.resolve(pro).then(
          (res) => {
            fulfilledCount++;
            result[index] = res;
            if (fulfilledCount === count) {
              resolve(result);
            }
          },
          (reason) => {
            reject(reason);
          }
        );
      }

      if (count === 0) {
        resolve(result);
      }
    });
  }

  static allSettled(pros) {
    const result = [];
    for (let pro of pros) {
      result.push(
        MyPromise.resolve(pro).then(
          (res) => ({
            status: FULFILLED,
            value: res,
          }),
          (reason) => ({
            status: REJECTED,
            value: reason,
          })
        )
      );
    }
    return MyPromise.all(result);
  }

  static race(pros) {
    return new MyPromise((resolve, reject) => {
      for (const pro of pros) {
        MyPromise.resolve(pro).then(resolve, reject);
      }
    });
  }
}

const p1 = new MyPromise((resolve) => {
  setTimeout(() => {
    resolve(2);
  }, 2000);
});
const p2 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    reject(1);
  }, 1000);
});
const pro = MyPromise.race([p1, p2]);

pro.then((res) => {
  console.log(res);
});
